package com.kaigao.demo.config;

import com.kaigao.demo.utils.SerializableUtil;
import org.apache.commons.lang.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.*;

/**
 * 类RedisClient.java的实现描述：TODO 类实现描述
 *
 * @author 吴昊 2018年3月17日08:39:05
 */
@Component
public class RedisClient {

    @Autowired
    private JedisPool        jedisPool;
    /** 7天的秒数 **/
    private static final int SEVEN_DAY_SECOND = 604800;

    /**
     * 方法描述:设置对象
     *
     * @param dbIndex redis数据库
     * @param key
     * @param value
     * @throws Exception
     * @author 吴昊 2018年3月17日08:39:15
     */
    public void set(Integer dbIndex, String key, String value) throws Exception {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            if (dbIndex != null) {
                jedis.select(dbIndex);
            }
            jedis.set(key, value);
        } finally {
            // 返还到连接池
            if (jedis != null) {
                jedis.close();
            }
        }
    }

    /**
     * 方法描述:设置key过期时间
     *
     * @author 吴昊 2018年3月17日08:39:21
     * @param dbIndex redis数据库
     * @param key
     * @param seconds
     */
    public void setExpire(Integer dbIndex, String key, Integer seconds) throws Exception {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            if (dbIndex != null) {
                jedis.select(dbIndex);
            }
            if (seconds != null) {
                jedis.expire(key, seconds);
            }
        } finally {
            // 返还到连接池
            if (jedis != null) {
                jedis.close();
            }
        }
    }

    /**
     * 方法描述:Redis INCR命令用于将键的整数值递增1。如果键不存在，则在执行操作之前将其设置为0。 如果键包含错误类型的值或包含无法表示为整数的字符串，则会返回错误。此操作限于64位有符号整数
     *
     * @author 吴昊 2018年3月17日08:39:28
     * @param dbIndex redis数据库
     * @param key
     */
    public void incr(Integer dbIndex, String key) throws Exception {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            if (dbIndex != null) {
                jedis.select(dbIndex);
            }
            jedis.incr(key);
            // 设置过期时间为7天后
            int secondsLeftToday = (int) (SEVEN_DAY_SECOND
                    - (DateUtils.getFragmentInMilliseconds(Calendar.getInstance(), Calendar.DATE)
                    / 1000));
            jedis.expire(key, secondsLeftToday);
        } finally {
            // 返还到连接池
            if (jedis != null) {
                jedis.close();
            }
        }
    }

    /**
     * 方法描述:查找所有匹配给定的模式的键
     *
     * @author 吴昊 2018年3月17日08:39:35
     * @param dbIndex redis数据库
     * @param redisKey pattern 的表达式,*表示多个，？表示一个
     * @return
     */
    public Map<String, Object> getByKeys(Integer dbIndex, String redisKey) throws Exception {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            if (dbIndex != null) {
                jedis.select(dbIndex);
            }
            Set<String> keys = jedis.keys(redisKey);
            Map<String, Object> map = new HashMap<>();
            if (keys != null && keys.size() > 0) {
                for (String key : keys) {
                    map.put(key, jedis.get(key));
                }
            }
            return map;
        } finally {
            // 返还到连接池
            if (jedis != null) {
                jedis.close();
            }
        }
    }

    /**
     * 方法描述:删除所有匹配给定的模式的键
     *
     * @author 吴昊 2018年3月17日08:39:43
     * @param dbIndex redis数据库
     * @param redisKey pattern 的表达式,*表示多个，？表示一个
     * @return
     */
    public void delByKeys(Integer dbIndex, String redisKey) throws Exception {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            if (dbIndex != null) {
                jedis.select(dbIndex);
            }
            Set<String> keys = jedis.keys(redisKey);
            if (keys != null && keys.size() > 0) {
                for (String key : keys) {
                    jedis.del(key);
                }
            }
        } finally {
            // 返还到连接池
            if (jedis != null) {
                jedis.close();
            }
        }
    }

    /**
     * 方法描述:设置key的有效期
     *
     * @author 吴昊 2018年3月17日08:41:15
     * @param dbIndex redis数据库
     * @param key
     * @param seconds
     */
    public void expire(Integer dbIndex, String key, int seconds) throws Exception {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            if (dbIndex != null) {
                jedis.select(dbIndex);
            }
            jedis.expire(key, seconds);
        } finally {
            // 返还到连接池
            if (jedis != null) {
                jedis.close();
            }
        }
    }

    /**
     * 方法描述:设置对象
     *
     * @param dbIndex redis数据库
     * @param key 键
     * @param seconds 缓存秒数
     * @param value 值
     * @throws Exception
     * @author leon 2017年5月3日 下午2:40:32
     */
    public void set(Integer dbIndex, String key, Integer seconds, String value) throws Exception {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            if (dbIndex != null) {
                jedis.select(dbIndex);
            }
            if (seconds == null) {
                jedis.set(key, value);
            } else {
                jedis.setex(key, seconds, value);
            }
        } finally {
            // 返还到连接池
            if (jedis != null) {
                jedis.close();
            }
        }
    }

    /**
     * 方法描述:获取对象
     *
     * @param dbIndex redis数据库
     * @param key 键
     * @return
     * @throws Exception
     * @author 吴昊 2018年3月17日08:41:25
     */
    public String get(Integer dbIndex, String key) throws Exception {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            if (dbIndex != null) {
                jedis.select(dbIndex);
            }
            return jedis.get(key);
        } finally {
            // 返还到连接池
            if (jedis != null) {
                jedis.close();
            }
        }
    }

    /**
     * 方法描述:设置对象
     *
     * @param dbIndex redis数据库
     * @param key
     * @param value
     * @throws Exception
     * @author 吴昊 2018年3月17日08:41:33
     */
    public void setObject(Integer dbIndex, String key, Object value) throws Exception {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            if (dbIndex != null) {
                jedis.select(dbIndex);
            }
            jedis.set(key.getBytes(), SerializableUtil.toByteArray(value));
        } finally {
            // 返还到连接池

            if (jedis != null) {
                jedis.close();
            }
        }
    }

    /**
     * 方法描述:设置对象
     *
     * @param dbIndex redis数据库
     * @param key 键
     * @param seconds 缓存秒数
     * @param value 值
     * @throws Exception
     * @author 吴昊 2018年3月17日08:41:39
     */
    public void setObject(Integer dbIndex, String key, int seconds, Object value) throws Exception {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            if (dbIndex != null) {
                jedis.select(dbIndex);
            }
            jedis.setex(key.getBytes(), seconds, SerializableUtil.toByteArray(value));
        } finally {
            // 返还到连接池

            if (jedis != null) {
                jedis.close();
            }
        }
    }

    /**
     * 方法描述:删除元素
     *
     * @param dbIndex redis数据库
     * @author 吴昊 2018年3月17日08:41:47
     * @param key
     * @throws Exception
     */
    @Async
    public boolean delObject(Integer dbIndex, String key) throws Exception {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            if (dbIndex != null) {
                jedis.select(dbIndex);
            }
            if (jedis.exists(key)) {
                return jedis.del(key) > 0;
            }
            return true;
        } finally {
            // 返还到连接池
            if (jedis != null) {
                jedis.close();
            }
        }
    }

    /**
     * 方法描述:redis HyperLogLog输入元素
     *
     * @author 吴昊 2018年3月17日08:41:55
     * @param dbIndex redis数据库
     * @param key
     * @param value
     */
    public Long pfadd(Integer dbIndex, String key, String value) throws Exception {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            if (dbIndex != null) {
                jedis.select(dbIndex);
            }
            Long pfadd = jedis.pfadd(key, value);
            // 设置过期时间为7天后
            int secondsLeftToday = (int) (SEVEN_DAY_SECOND
                    - (DateUtils.getFragmentInMilliseconds(Calendar.getInstance(), Calendar.DATE)
                    / 1000));
            jedis.expire(key, secondsLeftToday);
            return pfadd == null ? 0 : pfadd;
        } finally {
            // 返还到连接池
            if (jedis != null) {
                jedis.close();
            }
        }
    }

    /**
     * 方法描述:redis HyperLogLog输入元素
     *
     * @author 吴昊 2018年3月3日10:38:12
     * @param dbIndex redis数据库
     * @param key
     * @param value
     */
    public Long pfaddNoExpire(Integer dbIndex, String key, String value) throws Exception {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            if (dbIndex != null) {
                jedis.select(dbIndex);
            }
            return jedis.pfadd(key, value);
        } finally {
            // 返还到连接池
            if (jedis != null) {
                jedis.close();
            }
        }
    }

    /**
     * 方法描述:统计key在HyperLogLog中的基数 </br>
     * 基数：集合中不同元素的数量。比如 {‘apple’, ‘banana’, ‘cherry’, ‘banana’, ‘apple’} 的基数就是 3</br>
     * 估算值：算法给出的基数并不是精确的，可能会比实际稍微多一些或者稍微少一些，但会控制在合理的范围之内
     *
     * @author 吴昊 2018年3月17日08:44:18
     * @param dbIndex redis数据库
     * @param key
     * @return
     */
    public long pfcount(Integer dbIndex, String key) throws Exception {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            if (dbIndex != null) {
                jedis.select(dbIndex);
            }
            return jedis.pfcount(key);
        } finally {
            // 返还到连接池
            if (jedis != null) {
                jedis.close();
            }
        }
    }

    /**
     * 方法描述:模糊查询指定的键，返回键的集合
     *
     * @author 吴昊 2018年3月17日08:46:52
     * @param dbIndex redis数据库
     * @param redisKey pattern 的表达式,*表示多个，？表示一个
     * @return
     */
    public Set<String> getKeysByKeywords(Integer dbIndex, String redisKey) throws Exception {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            if (dbIndex != null) {
                jedis.select(dbIndex);
            }
            return jedis.keys(redisKey);
        } finally {
            // 返还到连接池
            if (jedis != null) {
                jedis.close();
            }
        }
    }

    /**
     * 方法描述:查找所有匹配给定的模式的键并统计所有键的基数
     *
     * @author 吴昊 2018年3月17日08:48:24
     * @param dbIndex redis数据库
     * @param redisKey
     * @return
     */
    public Map<String, Object> getPfcountByKeys(Integer dbIndex, String redisKey) throws Exception {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            if (dbIndex != null) {
                jedis.select(dbIndex);
            }
            Set<String> keys = jedis.keys(redisKey);
            Map<String, Object> map = new HashMap<>();
            if (keys != null && keys.size() > 0) {
                for (String key : keys) {
                    map.put(key, jedis.pfcount(key));
                }
            }
            return map;
        } finally {
            // 返还到连接池
            if (jedis != null) {
                jedis.close();
            }
        }
    }

    /**
     * 存入 set 集合
     *
     * @author 吴昊
     * @time 2018年3月17日08:48:40
     * @param key
     * @param value
     * @param dbIndex
     * @param seconds
     * @return void
     */
    public void sAdd(String key, String value, Integer dbIndex, Long seconds) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            if (dbIndex != null) {
                jedis.select(dbIndex);
            }
            jedis.sadd(key, value);
            if (seconds != null) {
                jedis.expire(key, seconds.intValue());
            }
        } finally {
            // 返还到连接池
            jedis.close();
        }
    }

    /**
     * 获取 所有成员 set
     *
     * @author 吴昊
     * @time 2018年3月17日08:48:49
     * @param key
     * @param dbIndex
     * @return java.util.Set<java.lang.String>
     */
    public Set<String> smembers(String key, Integer dbIndex) {
        Jedis jedis = null;
        Set<String> result = new HashSet<>();
        try {
            jedis = jedisPool.getResource();
            if (dbIndex != null) {
                jedis.select(dbIndex);
            }

            result = jedis.smembers(key);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            jedis.close();
        }
        return result;
    }

    /**
     * 删除集合中元素
     *
     * @author 吴昊
     * @time 2018年3月17日08:49:51
     * @param key
     * @param value
     * @param dbIndex
     * @return void
     */
    public void srem(String key, String value, Integer dbIndex) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            if (dbIndex != null) {
                jedis.select(dbIndex);
            }
            jedis.srem(key, value);
        } finally {
            // 返还到连接池
            jedis.close();
        }
    }
}
